package com.yunchang.spring.visitor.core.controller;

import com.yunchang.spring.visitor.core.handler.exception.IExceptionHandler;
import com.yunchang.spring.visitor.core.handler.request.IRequestHandler;
import com.yunchang.spring.visitor.core.handler.response.IResponseHandler;
import com.yunchang.spring.visitor.core.invoker.IServiceInvoker;
import com.yunchang.spring.visitor.core.invoker.InvokerCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * <pre>
 * 基于service和method名进行反射调用对应service方法的基类Controller。
 * 封装了请求处理过程中的异常处理，以及返回格式的统一处理。
 * 请求入参：
 *      1.url中必须带有service和method名字，用于反射调用对应的带ReflectiveService注解的service方法
 *      2.http请求(GET、POST表单)的所有参数，默认会转为Map类型，传入对应方法；
 *        如果方法的@ReflectiveMethod注解指定了参数列表params，会将Map中的参数依次按声明顺序传入
 *      3.http请求(POST JSON/XML等)的输入流的数据，默认会转为String类型，传入对应方法；
 *        比如方法只接受application/json类型的内容，方法的@ReflectiveMethod注解的contentType需要设置成
 *        {@link com.yunchang.spring.visitor.core.constant.ContentType#JSON}
 * 请求返回：
 *      1.如果反射的service方法正常返回，则会调用IResponseHandler对应的实现类，统一处理返回内容
 *      2.如果反射的service方法抛出异常，则会调用IExceptionHandler对应的实现类，统一处理返回内容
 * 缓存：
 *      1.如果方法的@ReflectiveMethod注解cacheResult为true，表明该方法支持缓存。
 *        则请求同一个service的同一个method(参数完全相同)的情况下，后续几次请求会优先从缓存中查询方法的返回结果。
 *        如果存在则直接返回，如果缓存查询为空，则反射调用对应service方法，并将结果缓存起来。
 *      2.MapServiceCache是默认的缓存实现，支持设定缓存大小，LRU的淘汰策略。
 *        需要自定义缓存类，可以实现IServiceCache接口，并将对应实现类添加进容器配置即可，并将MapServiceCache从容器配置中去掉。
 * </pre>
 * Created by jasontujun on 2018/1/10.
 *
 * @see com.yunchang.spring.visitor.core.annotation.ReflectiveService
 * @see com.yunchang.spring.visitor.core.annotation.ReflectiveMethod
 * @see IResponseHandler
 * @see IExceptionHandler
 */
public abstract class AReflectiveController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private IServiceInvoker defaultInvoker;

    @Resource
    private IExceptionHandler defaultExceptionHandler;

    @Resource
    private IResponseHandler defaultResponseHandler;

    @Resource
    private IRequestHandler defaultRequestHandler;

    /**
     * 获取IServiceInvoker实现类。
     * 子类可以覆盖此方法，替换成自定义的IServiceInvoker实现类。
     *
     * @return 返回IServiceInvoker实现类
     */
    protected IServiceInvoker getInvoker() {
        return defaultInvoker;
    }

    /**
     * 使用默认的IResponseHandler和IExceptionHandler，反射调用对应service方法。
     *
     * @param request  HttpRequest
     * @param response HttpResponse
     * @param service  service名
     * @param method   方法名
     * @return 如果调用正常，返回IResponseHandler处理后的返回结果；如果出现异常，返回IExceptionHandler处理后的返回结果
     */
    protected Object invokeService(final HttpServletRequest request,
                                   final HttpServletResponse response,
                                   final String service,
                                   final String method) {
        return invokeService(request, response, "", service, method);
    }

    /**
     * 使用默认的IResponseHandler和IExceptionHandler，反射调用对应service方法。
     *
     * @param request  HttpRequest
     * @param response HttpResponse
     * @param service  service名
     * @param method   方法名
     * @return 如果调用正常，返回IResponseHandler处理后的返回结果；如果出现异常，返回IExceptionHandler处理后的返回结果
     */
    protected Object invokeService(final HttpServletRequest request,
                                   final HttpServletResponse response,
                                   final String namespace,
                                   final String service,
                                   final String method) {
        return invokeService(request, response, namespace, service, method,
                defaultResponseHandler);
    }

    /**
     * 使用自定义的IResponseHandler，默认的IExceptionHandler，反射调用对应service方法。
     *
     * @param request         HttpRequest
     * @param response        HttpResponse
     * @param service         service名
     * @param method          方法名
     * @param responseHandler 自定义IResponseHandler
     * @return 如果调用正常，返回IResponseHandler处理后的返回结果；如果出现异常，返回IExceptionHandler处理后的返回结果
     */
    protected Object invokeService(final HttpServletRequest request,
                                   final HttpServletResponse response,
                                   final String namespace,
                                   final String service,
                                   final String method,
                                   final IResponseHandler responseHandler) {
        return invokeService(request, response, namespace, service, method,
                responseHandler, defaultExceptionHandler);
    }

    /**
     * 使用自定义的IResponseHandler和IExceptionHandler，反射调用对应service方法。
     *
     * @param request          HttpRequest
     * @param response         HttpResponse
     * @param service          service名
     * @param method           方法名
     * @param responseHandler  自定义IResponseHandler
     * @param exceptionHandler 自定义IExceptionHandler
     * @return 如果调用正常，返回IResponseHandler处理后的返回结果；如果出现异常，返回IExceptionHandler处理后的返回结果
     */
    protected Object invokeService(final HttpServletRequest request,
                                   final HttpServletResponse response,
                                   final String namespace,
                                   final String service,
                                   final String method,
                                   final IResponseHandler responseHandler,
                                   final IExceptionHandler exceptionHandler) {
        final long startTime = System.currentTimeMillis();
        final Object[] finalResult = new Object[1];
        getInvoker().invoke(namespace, service, method, request, defaultRequestHandler,
                new InvokerCallback() {
                    @Override
                    public void onResponse(HttpServletRequest request, String namespace, String serviceName, String methodName,
                                           Object result, Map<String, String> params, String httpContent) throws Exception {
                        finalResult[0] = responseHandler.handleResponse(request, response,
                                service, method, System.currentTimeMillis() - startTime, result, params, httpContent);
                    }

                    @Override
                    public void onException(HttpServletRequest request, String namespace, String serviceName,
                                            String methodName, Exception e, Map<String, String> params, String httpContent) {

                        finalResult[0] = exceptionHandler.handleError(request, response, System.currentTimeMillis() - startTime, e, params, httpContent);;
                    }
                });
        return finalResult[0];
    }
}
